package YAPserver;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
import java.util.Map.Entry;

import YAPserver.logic.Input.MoveCommand;

import YAPenvironment.YAPconstant;
import YAPenvironment.YAPconstant.Command;
import YAPenvironment.YAPerrorCode;

/**
 * The actual "client" on the server.
 * Manages all information concerning a client.
 * Manages the TCP-input from a client.
 */
public class ClientHandler implements Runnable{
	
	private static String sep = YAPconstant.C_SEPARATOR;
	private static String nl = YAPconstant.C_NEWLINE;

	private final Socket m_socket;
	
	private String m_name;
	private InetAddress inetAddress;
	private int udpPort;
	/* inGameId: the wall the client is assigned to/ has picked, identification in gameLogic*/
	private int inGameId;
	private Lobby lobby;
	private CheckConnection checkConnection;
	
//	private Thread m_clientHandlerThread;
	volatile boolean run = true;
	private volatile boolean isConnected = true;
	private boolean ready=false;

	private BufferedReader bufferedReader;
	private PrintWriter printWriter;

	/**
	 * Initializes the socket, printWriter and bufferedReader.
	 * If an exception is thrown during this process, it will not 
	 * parse incoming messages.
	 *  <p>
	 *  If everything goes well: The sockets are initialized, the name is accepted,
	 *  client registered on the server,  high score created, clientHandlerThread started, checkConnection started,
	 *  the new playerList are pushed out.
	 *  
	 * @param sClient
	 */
	public ClientHandler(Socket sClient){
		this.m_socket=sClient;
		
		if(!initializeSocket(m_socket)){
			System.out.println("Client-Socket: "+ m_socket+" couldn't be initialised");
			return;
		}
		
		if(YAPserver.connectedClients.size() >= YAPserver.maxConnection){
			try{
				writeMessage(Command.connect+sep+"-22");
			}catch (Exception e){
				if( YAPconstant.C_DEBUG){ e.printStackTrace(); }
			}
			System.out.println("Server is full. Couldn't accept another client.");
		}
		
		if(!firstConnection()){
			return;
		}
		
		
		/*
		 * Client was accepted
		 */
		YAPserver.connectedClients.add(this);
		System.out.println(m_name+" successfully connected to the server. Socket: "+m_socket+" UDPPort: "+udpPort);
		
		HighScore.createHighScore(m_name);
		
		new Thread(this).start();

		checkConnection = new CheckConnection(this);
		isConnected=true;
		YAPserver.getGUI().add(this);
		checkConnection.start();
		
		pushPlayerList();
	}
	/**
	 * Sets the bufferedReader and printWriter.
	 * @param socket
	 * @catch IOException
	 */
	private boolean initializeSocket(Socket socket){
		try {
			bufferedReader = new BufferedReader( new InputStreamReader( socket.getInputStream()));
			
			printWriter =  new PrintWriter( new OutputStreamWriter( socket.getOutputStream()));
				  	  
			this.inetAddress = socket.getInetAddress();
			
			return true;
		} catch (IOException e) {
			if(YAPconstant.C_DEBUG){ e.printStackTrace(); }
			System.out.println("NOT able to initialise socket");
		}
		return false;
	}
	/**
	 * @return inetAddress of the connected client
	 */
	public InetAddress getInetAddress(){
		return inetAddress;
	}
	/**
	 * @return true if the client is still connected
	 */
	public boolean isConnected(){
		return isConnected;
	}
	/**
	 * @param i boolean the state the connection should be set to
	 */
	public void setConnected(boolean i){
		isConnected=i;
	}
	/**
	 * @return the client's socket
	 */
	public Socket getSocket(){
		return m_socket;
	}
	/**
	 * @return the client's name
	 */
	public String getName(){
		return m_name;
	}
	/**
	 * Sets the name of the client.
	 * @param name
	 */
	private void setName(String name){
		m_name=name;
	}
	/**
	 * Shows if the client is ready. (Returns false, if he's not in a lobby.)
	 * @return true if the client is ready
	 */
	public boolean isReady(){
		return ready;
	}
	/**
	 * Shows if the client is in a lobby or not.
	 * @return true if the client is in a lobby
	 */
	public boolean isInLobby(){
		if(lobby==null)
			return false;
		return true;
	}
	/**
	 * Gets the gameId of the client. (If not in a lobby -1).
	 * The gameId corresponds to the side of the field that has been picked. 
	 * @return inGameId
	 */
	public int getGameId(){
		return inGameId;
	}
	/**
	 * Sets the inGameId.
	 * @param id
	 */
	public void setGameId(int id){
		inGameId=id;
	}
	/**
	 * Returns the UDP-Port the client is listening to.
	 * @return the UDP-Port the client is listening to
	 */
	public int getUdpPort(){
		return udpPort;
	}
	
	@Override
	/**
	 * While an object of this class is instantiated
	 * this method will wait for an incoming message from his client
	 * over the socket.
	 * <p>
	 * After receiving the message it will be parsed and the correct answer given.
	 */
	public void run() {
		while(run){
			try {
				if(m_socket!=null){
					String strMsg=readMessage(m_socket);
					/*
					 * Here is where the magic happens!
					 */
					if (strMsg!=null&&strMsg.contains(YAPconstant.C_CMD_SEPARATOR)){
						String[] lst = strMsg.split(YAPconstant.C_CMD_SEPARATOR);
						for (int i = 0; i < lst.length; i++){
							if ((lst[i].trim() != "") || (lst[i] != null)){
								parseMsg(lst[i]);
							}
						}
					}
				}
			}catch(SocketException e){
				//ignore
			}
			catch (Exception e) {
				if(YAPconstant.C_DEBUG){ e.printStackTrace(); }
				System.out.println("Couldn't receive message from: "+m_name);
			}
		}
	}
	
	/**
	 * Receives the first incoming message from a client: <p>
	 * -cuts commandseparator off <p>
	 * -checks if the command is right <p>
	 * -checks the name <p>
	 * -changes the name if needed <p>
	 * -adds the new client to the connected clients <p>
	 * -sets the highscore <p>
	 * -sleeps for 200 ms <p>
	 * -pushes playerlist <p>
	 * -starts the thread which checks the connection
	 */
	private boolean firstConnection() {
		try{
			String msg=readMessage(m_socket);

			// Cut the last char off, if it is YAPconstant.cmgSeparator
			if (msg.charAt(msg.length()-1) == YAPconstant.C_CMD_SEPARATOR.toCharArray()[0]){
				msg = msg.substring(0, msg.length()-1);
			}
			
			//Check if the incoming message matches the Protocol:
			if(!msg.startsWith(Command.connect.toString())) {
				//Command is not connect
				System.out.println(YAPerrorCode.getErrorMsg(-99));
				writeMessage(Command.connect+ sep + "-99");
				return false;
			}
			if(msg.split(sep).length!=3){
				//Msg length is not 3
				System.out.println("Command does not match protocol: msg.length");
				writeMessage(Command.connect+ sep + "-99");
				return false;
			}
			try{
				udpPort = Integer.parseInt( msg.split(sep)[2] );
			}catch (NumberFormatException exc){
				//udpPort not an int
				System.out.println(YAPerrorCode.getErrorMsg(-99));
				writeMessage(Command.connect+ sep + "-99");
				return false;
			}

			if( udpPort < YAPconstant.C_UDP_SOCKET || udpPort > YAPconstant.C_MAXPORT){
				//UDPport out of range
				System.out.println(YAPerrorCode.getErrorMsg(-200));
				writeMessage(Command.connect+ sep + "-200");
				return false;
			}
			
			
			msg = msg.split(sep)[1];
			int i = checkName(msg);
			if(i!=0){
				msg=forceChangeName(msg);
			}
			m_name=msg;
			
			writeMessage(Command.connect.toString()+sep+"0"+sep+m_name);
		
			try{
				Thread.sleep(200);
			}catch(InterruptedException e){
				//Client may not get newest player- and lobbyList
				if(YAPconstant.C_DEBUG){ e.printStackTrace(); }
			}
			
			return true;
		}

		//=disconnect() without pushPlayerlist
		catch (Exception e) {
			if(YAPconstant.C_DEBUG){ e.printStackTrace(); }
			System.out.println("Error receiving the first data package from the client. Closing socket");
			try {
				writeMessage(Command.connect.toString()+sep+"-99");
				m_socket.close();
			} catch (IOException e1) {
				System.out.println("Couldn't close a not yet connected client's socket");
			}
			return false;
		}
	}
	
	/**
	 * The parsing method, which splits the incoming strings, extracts the command,
	 * checks for authority, calculates the result and sends it to the client(s)
	 * back.
	 * @param strMsg
	 */
	public void parseMsg(String strMsg){
		if(strMsg==null || strMsg.equals("") || strMsg.length()<5)
			return;
		String cmd=strMsg.split(sep)[0];
		strMsg = strMsg.replaceFirst(cmd+sep,"");
		Command command;
		
		try{
			/*
			 * cmd = command (command is the Enum of cmd)
			 * strMsg = Input to parse
			 */
			command = Command.valueOf(cmd);
		
			switch(command){

			case sendKey:{
				sendKey(strMsg);
				break;
			}
			case noMove:{
				if(lobby!=null && lobby.isStarted() && inGameId >= 0)
					lobby.getGame().setCommand(inGameId, MoveCommand.NOMOVE);
				break;
			}
			case changeName:{
				int i=checkName(strMsg);
				if(i==0){
					setName(strMsg);
					HighScore.createHighScore(strMsg);
				}
				writeMessage(cmd+sep+i+sep+m_name);
				pushPlayerList();
				break;
			}
			case createLobby:{
				createLobby(strMsg);
				pushLobbyInformation();
				pushMsgToLobby("joined");
				pushLobbyList();
				break;				
			}
			case joinLobby:{
				int i=joinLobby(strMsg);
				writeMessage(cmd+sep+i+sep+strMsg);
				pushLobbyInformation();
				pushMsgToLobby("joined");
				pushLobbyList();
				break;
			}
			case leaveLobby:{
				int i=leaveLobby();
				writeMessage(cmd+sep+i);
				break;
			}
			case getLobbyList:{
				sendLobbyList();
				break;
			}
			case getPlayerList:{
				sendPlayerList();
				break;
			}case sendMsg:{
				sendMsg(strMsg);
				break;
			}
			case sendMsgTo:{
				sendMsgTo(strMsg);
				break;
			}
			case disconnect:{
				disconnect();
				break;
			}
			case isAliveClient:{
				writeMessage(Command.isAliveClient+sep+"0");
				break;
			}
			case isAliveServer:{
				isConnected=true;
				break;
			}
			case getLobbyInformation:{
				sendLobbyInformation(strMsg);
				break;
			}
			case setReady:{
				setReady();
				break;
			}
			case setUnReady:{
				setUnready();
				break;
			}
			case pickSide:{
				pickSide(strMsg);
				break;
			}
			case startGame:{
				/*
				 * Game starts as soon as everyone is ready
				 */
				break;
			}
			case createPlayField:{
				createPlayGround(strMsg);
				break;
			}
			case pause:{
				pause();
				break;
			}
			case getHighScore:{
				writeMessage(cmd+sep+"0"+sep+nl+HighScore.getHighScore());
				break;
			}
			default:{
				writeMessage(Command.commandNotFound.toString()+sep+"-99");
				break;
			}
			}
		}catch(Exception e){
			try{
				writeMessage(Command.commandNotFound.toString()+sep+"-99");
			} catch (Exception e2){
			}
			if(YAPconstant.C_DEBUG){ e.printStackTrace(); }
			System.out.println("Couldn't parse Message from: "+m_name);
		}
	}
	
	/**
	 * If the Player is in a Lobby:
	 * checks if the game is started and can be paused.
	 * pauses or unpauses the game.
	 */
	private void pause(){
		if(lobby != null && lobby.isStarted()){
			if(lobby.isPaused()){
				lobby.pauseGame();
				pushUnPause();
			}
			else{
				lobby.pauseGame();
				pushPause();
			}
		}
	}
	/**
	 * If a game can be paused. The information that it is paused will be pushed to every client.
	 */
	private void pushPause(){
		if(lobby != null){
			for(ClientHandler cH: lobby.getPlayers()){
				try{
					cH.writeMessage(Command.pause + sep + "0" + sep + this.getName());
				}catch (Exception e){
					if(YAPconstant.C_DEBUG) { e.printStackTrace(); }
				}
			}
		}
	}
	
	/**
	 * pushes the "pause 1 name" command to every client according to Protocol 
	 */
	private void pushUnPause(){
		if(lobby != null){
			for(ClientHandler cH: lobby.getPlayers()){
				try{
					cH.writeMessage(Command.pause + sep + "1" + sep + this.getName());
				}catch (Exception e){
					if(YAPconstant.C_DEBUG) { e.printStackTrace(); }
				}
			}
		}
	}
	
	/**
	 * Sets the direction of the this client's slider in the game.
	 * @param strMsg String which is parsed as "left" or "right"
	 */
	void sendKey(String strMsg) {
		if(lobby!=null && lobby.isStarted() && inGameId>=0){
			if(strMsg.equals("left")){
				lobby.getGame().setCommand(inGameId, MoveCommand.LEFT);
			}
			else if(strMsg.equals("right")){
				lobby.getGame().setCommand(inGameId, MoveCommand.RIGHT);
			}
		}
	}
	/**
	 * Changes the chosen side of a player if the move is legal.
	 * @param msg
	 */
	private void pickSide(String msg){
		if(lobby!=null && !lobby.isStarted()){
			int i = Integer.parseInt(msg);
			if(lobby.pickSide(inGameId, i)){
//				System.out.println("inGameId "+inGameId+ "neu "+i);
				//the gameId is the wall the client has picked
				inGameId=i;
				pushLobbyInformation();
			}
		}else{
			try{
				writeMessage(Command.pickSide.toString()+sep+"-45");
			}catch(Exception e){
			}
		}
	}
	/**
	 * Changes the playground using the given parameters.
	 * @param strMsg
	 * @throws IOException
	 */
	private void createPlayGround(String strMsg) throws IOException{
		if(lobby!=null){
			if(lobby.getLobbyHost()==this){
				int walls = Integer.parseInt(strMsg.split(sep)[1]);
				double radReg = Double.parseDouble(strMsg.split(sep)[2]);
				int lives = Integer.parseInt(strMsg.split(sep)[3]);
				lobby.changePlayField(walls, radReg, lives, false);
				pushLobbyInformation();
			}
			else writeMessage(Command.createPlayField.toString()+ sep + "-9");
		}
		else{
			writeMessage(Command.createPlayField.toString() + sep + "-15");
		}
	}
	/**
	 * Sends to every client his individual information of the lobby (with his own side first then sides taken).
	 * @param strMsg
	 * @throws Exception
	 */
	void sendLobbyInformation(String strMsg) throws Exception{
		if(lobby!=null){
			writeMessage(Command.getLobbyInformation+sep+"0"+sep+lobby.toLobbyInformationString()+getSides(this, lobby)+nl+lobby.getLifes());
		}
		else{
			if(YAPserver.lobbyList.containsKey(strMsg)){
				Lobby lby=YAPserver.lobbyList.get(strMsg);
				writeMessage(Command.getLobbyInformation+sep+"0"+sep+lby.toLobbyInformationString());	//YAPserver.lobbyList.get(strMsg).toLobbyInformationString());
			}
			else{
				writeMessage(Command.getLobbyInformation+sep+"-15"+sep);
			}
		}		
	}
	/**
	 * Sets a clients status to ready if he is in a lobby.
	 * @throws Exception
	 */
	private void setReady() throws Exception{
		if(lobby!=null && !lobby.isStarted()){
			if(this.ready)
				writeMessage(Command.setReady.toString()+sep+"-21");
			else{
				this.ready=true;
				pushLobbyInformation();
			
				if(lobby.isFull() && lobbyIsReady()){
					System.out.println("All players in the lobby "+lobby.getName()+" are ready");
					/**
					 * starting the GAME
					 */
					lobby.startGame();
				}
			}
		}
		else{
			writeMessage(Command.setReady.toString()+sep+"-15");
		}
	}
	/**
	 * Sets a client status to not-ready if he is in a lobby.
	 * @throws Exception
	 */
	void setUnready() throws Exception{
		if(lobby!=null){
			if(this.ready){
				this.ready=false;
				pushLobbyInformation();
			}
			else{
				writeMessage(Command.setUnReady.toString()+sep+"-22");
			}
				
		}
		else{
			writeMessage(Command.setUnReady.toString()+sep + "-15");
		}
	}
	/**
	 * @return whether every person in the lobby is ready
	 */
	private boolean lobbyIsReady(){
		for(ClientHandler cH: lobby.getPlayers()){
			if(!cH.isReady())
				return false;
		}
		return true;
	}
	/**
	 * Pushes a message to every client in the lobby (lobby-chat)
	 * @param msg
	 */
	private void pushMsgToLobby(String msg){
		if(lobby!=null)
			for(ClientHandler cH: lobby.getPlayers())
				try{
					cH.writeMessage(Command.sendMsg.toString()+sep+m_name+": "+msg+" the lobby "+lobby.getName());
				}catch (Exception e){
					
				}
	}
	/**
	 * Pushes the information of the lobby, after there have been changes, to all players.
	 */
	private void pushLobbyInformation() {
		if(lobby!=null){
			String string=lobby.toLobbyInformationString();
			for( ClientHandler cH: lobby.getPlayers())
				try{
					cH.writeMessage( Command.getLobbyInformation+sep+"0"+sep+string+cH.getSides(cH, lobby)+nl+lobby.getLifes());
				}catch(Exception e){
					if(YAPconstant.C_DEBUG){ e.printStackTrace(); }
				}
		}
		
	}
	/**
	 * @param cH
	 * @param lobby
	 * @return the string with the information which sides are taken (nl+ ownID + sep + takensides)
	 */
	private String getSides(ClientHandler cH, Lobby lobby){
		if(lobby==null)
			return "";
		String s="";
		s+=nl;
		s+=cH.getGameId();
		s+=sep;
		s+=lobby.getSides();
		
		return s;
	}
	/**
	 * Pushes the list of all players to every client who is not in a lobby.
	 * <p>
	 * @param cmd
	 * @throws Exception
	 */
	private void pushPlayerList(){
		String players=getPlayerList();
		for(ClientHandler cH: YAPserver.connectedClients){
			if(!cH.isInLobby()){
				try{
					cH.writeMessage(Command.getPlayerList.toString()+sep+nl+players);
				}catch (Exception e2){}
			}
		}
	}
	/**
	 * Sends the lobby-list to every client not in a game.
	 */
	void pushLobbyList(){ // quick fix
		String lobbyList = getLobbyList();		//lobbyList starts with \tInteger or \t0\t (List)
		for(ClientHandler cH: YAPserver.connectedClients){
			if(!cH.isInLobby())
				try{
					cH.writeMessage(Command.getLobbyList.toString()+lobbyList);
				}
				catch (Exception e){
				}
		}
	}
	/**
	 * Disconnects the client from the server.
	 * <p>
	 * Removes the client from the lobby, sets everything to null, closes the socket.
	 */
	void disconnect(){
		if(lobby!=null){
			lobby.removePlayer(this);
			pushLobbyInformation();
			pushLobbyList();
		}
		YAPserver.connectedClients.remove(this);
		pushPlayerList();
		lobby=null;
		m_name=null;
		run = false;
		try{
			m_socket.close();
		}catch (SocketException e){
			//ignore
		}
		catch (Exception e){
			if(YAPconstant.C_DEBUG){ e.printStackTrace(); }
		}
		YAPserver.getGUI().remove(this);
	}
	/**
	 * Sends a message to one specific person.
	 * If this person (located by name) is not on the server
	 * an errorCode will be returned (-100)
	 */
	private void sendMsgTo(String strMsg) throws Exception{
		String to=strMsg.split(sep)[0];
		String msg=strMsg.split(sep)[1];
		if(msg.length()>=50)
			msg.substring(0,50);
		for(ClientHandler cH: YAPserver.connectedClients)
			if(cH.getName().equalsIgnoreCase(to)){
				try{
					cH.writeMessage(Command.sendMsgFrom.toString()+sep+"0"+sep+ m_name+sep+msg);
					writeMessage(Command.sendMsgTo+sep+"0"+sep+cH.getName()+sep+msg);
				}catch(Exception exc){
					writeMessage(Command.sendMsgTo+sep+"-10");
				}
				return;
			}
		writeMessage(Command.sendMsgTo+sep+"-100");
	}
	/**
	 * Sends a message to every person in the lobby-overview.
	 * <p>
	 * @param strMsg
	 * @throws Exception
	 */
	private void sendMsg(String strMsg) throws Exception{
		if(strMsg.equals(""))
			return;
		if(strMsg.length()>=50)
			strMsg.substring(0,50);
		if(lobby!=null){
			for(ClientHandler cH: lobby.getPlayers()){
				//System.out.println("sendMsgto: "+cH.getName()+":*"+strMsg+"*");
				try{
					cH.writeMessage(Command.sendMsg.toString()+sep+m_name+": "+strMsg);
					if(askedForUltimateAnswer(strMsg)){
						cH.writeMessage(Command.sendMsg.toString()+sep+"Deep Thought: 42");
					}
				}catch(Exception e){}
			}
		}
		else{
			for(ClientHandler cH: YAPserver.connectedClients){
				if(!cH.isInLobby())
					try{
//						System.out.println("sendMsgto: "+cH.getName()+":*"+strMsg+"*");
						cH.writeMessage(Command.sendMsg.toString()+sep+m_name+": "+strMsg);
						if(askedForUltimateAnswer(strMsg)){
							cH.writeMessage(Command.sendMsg.toString()+sep+"Deep Thought: 42");
						}
					}catch(Exception exc){}
			}
		}
		
	}
	/**
	 * Returns true if a client has asked for the ultimate answer.
	 * @author Daniel
	 * @param s string message to test whether the answer has been asked for
	 * @return true if a client has asked for the ultimate answer
	 */
	private boolean askedForUltimateAnswer(String s){
		String[] words = s.split(" ");
		for(int i=0; i<words.length - 6; i++){
			if(words[i].contains("answer") &&
					words[i+1].contains("to") &&
					words[i+2].contains("life") &&
					words[i+3].contains("the") &&
					words[i+4].contains("universe") &&
					words[i+5].contains("and") &&
					words[i+6].contains("everything")){
				return true;
			}
		}
		return false;
	}
	/**
	 * Sends the list of all players to the client using this command.
	 * Gets the string of clients with {@link #getPlayerList()} and sends it to the clients.
	 * @param cmd
	 * @param socket
	 * @throws Exception
	 */
	private void sendPlayerList() throws Exception{
		String players=getPlayerList();
		writeMessage(Command.getPlayerList.toString()+sep+nl+players);
	}
	/**
	 * Adds every player on the server to a string ready to send.
	 */
	private String getPlayerList(){
		String players="";
		for(ClientHandler cH: YAPserver.connectedClients)
			players+=cH.getName()+nl;
		return players;
	}
	/**
	 * Sends the lobby-list to the client using this command
	 * by adding every lobby to one string.
	 * <p>
	 * @param strMsg
	 * @param cmd
	 * @throws Exception
	 */
	private void sendLobbyList() throws Exception{
		String string= getLobbyList();
		writeMessage(Command.getLobbyList.toString()+string);
	}
	/**
	 * Puts all lobbies with unstarted games into one string.
	 * 
	 * @return\t-2 if empty, \t0List otherwise
	 */
	private String getLobbyList(){
		if(YAPserver.lobbyList.isEmpty()){
			return sep+"-2";
		}
		String string=sep+"0"+sep;
		for(Entry<String, Lobby> e : YAPserver.lobbyList.entrySet()){
			if(e.getValue()!=null){
				if(!e.getValue().isStarted()){
					if(!e.getValue().isFull()){
						string+=e.getValue().toString()+nl;
					}
				}
			}
		}
		if( string.equals(sep+"0"+sep) )
			return sep+"-2";
		
		return string;
	}
	/**
	 * Checks if a lobby with the given parameters can be created.
	 * This lobby is thus created and this client is put in it.
	 * <p>
	 * @param strMsg
	 * @param cmd
	 * @throws Exception
	 */
	private void createLobby(String strMsg) throws Exception{
		int i=createLobbyCheck(strMsg);
		if(i!=0){
			writeMessage(Command.createLobby.toString()+sep+i+sep);
			return;
		}
		
		//i==0
		String lobbyName= createNewLobby(strMsg);
		inGameId=0;
		writeMessage(Command.createLobby.toString()+sep+i+sep+lobbyName+getSides(this, lobby));
	}
	/**
	 * Lets a client leave a lobby if the client is in one.
	 * @return errorCode
	 */
	private int leaveLobby() {
		
		if(lobby==null)
			return-47;
		
		//lobby!=null: The Player is in a Lobby
		pushMsgToLobby("left");
		lobby.removePlayer(this);
		pushLobbyInformation();
		lobby=null;
		ready=false;
		inGameId=-1;

		pushLobbyList();
		pushPlayerList();
		return 0;
	}
	/**
	 * Checks if a client is allowed to join the lobby.
	 * <p>
	 * If he is allowed, he will be added to the lobby and "0" will be returned.
	 * Otherwise the matching errorCode will replace the "0".
	 * @param strMsg
	 * @return errorCode
	 * @throws Exception
	 */
	private int joinLobby(String strMsg) throws Exception{
		if(lobby!=null){
			return-46;
		}
		
		if(YAPserver.lobbyList.containsKey(strMsg)){
			lobby=YAPserver.lobbyList.get(strMsg);
			if(!lobby.isFull() && !lobby.isStarted())
				inGameId=lobby.addPlayer(this);
			else{
				lobby=null;
				return -3;
			}
		}else{
			return -45;
		}
		return 0;
	}
	/**
	 * Creates a new lobby and adds the client to it.
	 * <p>
	 * @param strMsg
	 * @return errorCode
	 */
	private String createNewLobby(String strMsg) throws NumberFormatException{
		String lobbyName=strMsg.split(sep)[0];
		strMsg=strMsg.substring(lobbyName.length()+sep.length(),strMsg.length());
		int players=Integer.parseInt(strMsg);
		this.lobby = new Lobby(this, lobbyName,players);
		YAPserver.lobbyList.put(lobbyName,lobby);
		
		return lobbyName;
	}
	/**
	 * Checks if a lobby can be created with this command or not.
	 * <p>
	 * @param strMsg
	 * @return errorCode
	 */
	private int createLobbyCheck(String strMsg){ 
		if(lobby!=null){
			//bereits in einer Lobby
			return -46;
		}
		if(!strMsg.contains(sep)){
			return -10;
		}
		
		String lobbyName=strMsg.split(sep)[0];
		strMsg=strMsg.substring(lobbyName.length()+sep.length(),strMsg.length());
		
		if(lobbyName==null || lobbyName.length()<YAPconstant.C_MINNAMELENGTH)
			return -40;
		if(lobbyName.length()>YAPconstant.C_MAXNAMELENGTH)
			return -41;
		if(lobbyName.contains(" ") || lobbyName.contains(sep) || lobbyName.contains(nl))
			return -42;
		
		if(YAPserver.lobbyList.containsKey(lobbyName)){
			/*
			 * Lobby allready exists (with this name). 
			 */
			return -43;
		}	

		try{
			int players=Integer.parseInt(strMsg);
			if(players<YAPconstant.C_MINPLAYERS || players > YAPconstant.C_MAXPLAYERS)
				return -44;
		}catch(Exception e){
			return -44;
		}
		return 0;
	}
	/**
	 * This method checks if the name is written in a correct and acceptable form.
	 * @param strName
	 * @return int <0: errorCode, else 0 (=ok);
	 */
	public int checkName(String strName){
		if(strName.length()>YAPconstant.C_MAXNAMELENGTH)
			return -12;
		if(strName.length()<YAPconstant.C_MINNAMELENGTH)
			return -13;
		if(strName.contains(" ") || strName.contains(sep) || strName.contains(nl))
			return -11;
		/*
		 * name already taken?
		 */
		for( ClientHandler cH: YAPserver.connectedClients){
			if(cH.getName().equalsIgnoreCase(strName)){
				return -1;
			}
		}
		return 0;
	}
	/**
	 * Cuts or prolongs the name by putting the numbers 1, 2, 3, ... 11, 12
	 * at the end until the name is accepted and not taken. If the name is not taken no numbers are added to it.
	 * @param String Name
	 * @return String newName
	 */
	private String forceChangeName(String strName) {
		if(strName.contains(" ") )
				strName=strName.replaceAll(" ","");
		if(strName.contains(sep) )
			strName=strName.replaceAll(sep,"");
		if(strName.contains(nl))
			strName=strName.replaceAll(nl,"");
		if(strName.length()>YAPconstant.C_MAXNAMELENGTH)
			strName=strName.substring(0,YAPconstant.C_MAXNAMELENGTH);
		while(strName.length()<YAPconstant.C_MINNAMELENGTH)
			strName=strName+"1";
		int help=1;
		String helpName=new String(strName);
		while(!checkNameFree(helpName)){
			helpName=strName+help;
			help++;
		}
		return helpName;
	}
	/**
	 * @param strName
	 * @return if another connected client already has the same name
	 */
	boolean checkNameFree(String strName){
		for( ClientHandler cH: YAPserver.connectedClients){
			if(cH.getName().equalsIgnoreCase(strName)){
				return false;
			}
		}
		return true;
	}
	
	/**
	 * Writes the message with the printWriter.
	 * @param strMsg
	 * @throws IOException
	 */
	public synchronized void writeMessage(String strMsg) throws IOException {
		strMsg += YAPconstant.C_CMD_SEPARATOR;
//		if(!strMsg.startsWith("isAlive") && !strMsg.startsWith("sendP"))
//			System.out.println("Server Output: *"+strMsg+"*");
		if(strMsg.trim().equals("")||strMsg==null) return;
	    printWriter.println(strMsg);
	    printWriter.flush();
	  }
	/**
	 * Receives the message from the bufferedReader.
	 * @param socket
	 * @return the Incoming Message (Command + sep + parameter)
	 * @throws Exception
	 */
	private String readMessage(Socket socket) throws Exception {
			String nachricht = bufferedReader.readLine();
//			if(nachricht!=null&&!nachricht.startsWith("isAlive"))
//				System.out.println("Server Input: *"+nachricht+"*");
			return nachricht;
	  }
	@Override
	public String toString(){
		return m_name;
	}
}